from __future__ import division
from util import *
from math import log10
from itertools import imap, cycle

BOOSTER = 1.2

class Scorer:
    ''' Abstract base class for scorers.'''
    def score(self):
        raise NotImplementedError

class Cosine(Scorer):
    ''' Cosine scorer for Term and Wildcard queries.'''
    def __init__(self, index, termIDs):
        self.index = index
        if type(termIDs) == list:
            self.termIDs = set(termIDs)
        else:
            self.termIDs = [termIDs]
        #self.idf = max([index.idf(tid) for tid in self.termIDs])
        self.idf = log10(index.docnum / min([index.idfs[tid] for tid in self.termIDs]))
        #self.idf = max([log10(index.docnum / len(index.tfd[tid])) for tid in self.termIDs])
        #self.idf = log10(index.docnum / min([len(index.tfd[tid]) for tid in self.termIDs]))
        
    def score(self, docID):
        idx = self.index
        wtf = max([idx.tf(tid,docID) for tid in self.termIDs]) / idx.normTFs[docID]
        return wtf*self.idf
    
    @property
    def termslen(self):
        return len(self.termIDs)

class BM25(Scorer):
    def __init__(self, index, termIDs, B=0.75, K1=2.0):
        self.index = index
        if type(termIDs) == list:
            self.termIDs = set(termIDs)
        else:
            self.termIDs = [termIDs]
        self.K1 = K1
        self.B = B
        self.idf = log10(index.docnum / min([index.idfs[tid] for tid in self.termIDs]))
        
    def score(self, docID):
        idx = self.index
        maxwtf = max([idx.tf(tid,docID) for tid in self.termIDs])
        score = self.idf * maxwtf * (self.K1 + 1) / (maxwtf + self.K1 * (1 - self.B + self.B * idx.normTFs[docID]))
        return score

    @property
    def termslen(self):
        return len(self.termIDs)    
            
class LevenshteinCosine(Scorer):
    ''' Cosine scorer with levenshtein distance weight function. '''
    def __init__(self, index, termIDs, distances):
        self.index = index
        if type(termIDs) == list:
            self.termIDs = list(set(termIDs))
        else:
            self.termIDs = [termIDs]
        self.distances = distances
        self.idf = log10(index.docnum / min([index.idfs[tid] for tid in self.termIDs]))

    def score(self, docID):
        idx = self.index
        #wtf = max([idx.tf(tid, docID) * log10(1 + 10.0 / self.distances[tid]) for tid in self.termIDs]) / idx.normTFs[docID]
        wtf = max([idx.tf(tid, docID) / log10(self.distances[tid]+1.1) for tid in self.termIDs]) / idx.normTFs[docID]
        return wtf * self.idf
    
    @property
    def termslen(self):
        return len(self.termIDs)

class PageRankScorer(Scorer):
    ''' PageRank scorer '''
    def __init__(self, index, termIDs):
        self.index = index
        self.basescorer = Cosine(index, termIDs)
        self.maxpr = max(self.index.pagerank)
        
    def score(self, docID):
        return log10(1 + self.index.pagerank[docID]/self.maxpr) + self.basescorer.score(docID)
        #return self.index.pagerank[docID]*self.basescorer.score(docID)

class MNBScorer(Scorer):
    def __init__(self, index, termIDs, top1docid):
        self.index = index
        self.basescorer = Cosine(index, termIDs)
        self.targetcls = index.labmnb[top1docid]
        self.maxpr = max(self.index.pagerank)

    def score(self, docID):
        booster = BOOSTER if set(self.index.labmnb[docID]) & set(self.targetcls) != set([]) else 1
        return log10(1 + self.index.pagerank[docID]/self.maxpr) + booster * self.basescorer.score(docID)

class SVMScorer(Scorer):
    def __init__(self, index, termIDs, top1docid):
        self.index = index
        self.basescorer = Cosine(index, termIDs)
        self.targetcls = index.labsvm[top1docid]
        self.maxpr = max(self.index.pagerank)

    def score(self, docID):
        booster = BOOSTER if set(self.index.labsvm[docID]) & set(self.targetcls) != set([]) else 1
        return booster * self.basescorer.score(docID) + log10(1 + self.index.pagerank[docID]/self.maxpr)

class CompondScorer(Scorer):
    def __init__(self, index, scorers):
        self.index = index
        self.scorers = scorers
        
    @property
    def termslen(self):
        return sum([s.termslen for s in self.scorers])

    def score(self, docID):
        return sum([sc.score(docID) for sc in self.scorers])
        





    